package frame.laxcen.util;

import frame.laxcen.BaseFrame;
import frame.laxcen.exception.IllegalFrameFormatException;
import frame.laxcen.response.IResponse;
import util.ByteUtil;
import util.FlowUtil;
import util.Ref;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Predicate;

public class FrameUtil {

    public static boolean isErrorResponse(BaseFrame frame) {

        BaseFrame.ControlWord CW = frame.CW;

        return Byte.compare(CW.MessageType, (byte) 0) == 0 && CW.IsUpload && CW.MID == 0;
    }

    public static void printFrame(BaseFrame frame) {

        printFrame(frame.FrameBytes);
    }

    public static void printFrame(byte[] frame) {

        StringBuilder sb = new StringBuilder();

        for(int i = 0; i < frame.length; ++i) sb = sb.append(Integer.toHexString(frame[i] & 0xff)).append(' ');

        System.out.println(sb.toString());
    }

    public static <T extends IResponse> void printResponse(T response) {

        FlowUtil.silent(() -> response.explainToMap().forEach((k, v) -> System.out.println(k + " : " + v)));
    }

    public static String toHexString(byte[] bs) {

        StringBuilder sb = new StringBuilder();

        for(byte b : bs) sb.append(Integer.toHexString(Byte.toUnsignedInt(b)));

        return sb.toString();
    }

    public static String toHexString(short[] ss) {

        StringBuilder sb = new StringBuilder();

        for(short s : ss) sb.append(Integer.toHexString(Short.toUnsignedInt(s)));

        return sb.toString();
    }

    public static String toHexString(int[] is) {

        StringBuilder sb = new StringBuilder();

        for(int i : is) sb.append(Long.toHexString(Integer.toUnsignedLong(i)));

        return sb.toString();
    }

    public static void dispatch(BaseFrame frame, List<BaseFrame> trueResult, List<BaseFrame> falseResult, Predicate<BaseFrame> splitCondition) {

        if(splitCondition.test(frame)) trueResult.add(frame); else falseResult.add(frame);
    }

    public static void split(List<BaseFrame> frames, List<BaseFrame> trueResult, List<BaseFrame> falseResult, Predicate<BaseFrame> splitCondition) {

        for(BaseFrame frame : frames) if(splitCondition.test(frame)) trueResult.add(frame); else falseResult.add(frame);
    }

    public static List<BaseFrame> disjoint(byte[] block) {

        List<BaseFrame> baseFrames = new LinkedList<>();

        for(int i = 0, lastListLength = baseFrames.size(); i < block.length; ++i) {

            // i     -> 0xaa
            // i + 1 -> CW.H
            // i + 2 -> CW.L
            // i + 3 -> DataLength.H
            // i + 4 -> DataLength.L

            if(Byte.compare(block[i], (byte) 0xaa) != 0) continue;

            // HEAD.length = 1
            // CW.length = 2
            // DataLength.length = 2
            // Data.length = DataLength.value
            // CRC.length = 2

            int dataLength_H = i + 3, dataLength_L = i + 4;

            if(dataLength_L >= block.length) continue;

            int dataLength = ByteUtil.bytePairToInt(block[dataLength_H], block[dataLength_L]);

            int frameBegin = i, frameEndClosed = frameBegin + 4 + dataLength + 2;

            if(frameEndClosed >= block.length) continue;

            Ref<BaseFrame> ref = Ref.empty();

            try {

                ref.swap(new BaseFrame(Arrays.copyOfRange(block, frameBegin, frameEndClosed + 1)));

            } catch (IllegalFrameFormatException e) {

                e.printStackTrace();
            }

            if(ref.isPresent() && baseFrames.add(ref.get())) i = frameEndClosed;
        }

        return baseFrames;
    }
}
